feat(desktop): browser profiles for the preview browser - #7254
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
There was a problem hiding this comment.
Reviewed the web UI changes (RightPanelTabs, PreviewChromeRow/PreviewView, PreviewMoreMenu, IntegrationsSettings, menu.tsx) against the shared primitive contracts. Two consistency findings, both inline.
Posted via Macroscope — UI Consistency
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces a substantial cross-layer browser-profile capability with new persistent/ephemeral storage partitions, profile-aware webview configuration, settings CRUD, and destructive profile-data cleanup. It also adds a configurable default profile for new tabs, so the resulting product-default and data-isolation behavior requires human review. No code changes detected at You can add or adjust custom eligibility rules. Learn more. |
b7395b6 to
fe1bc48
Compare
fe1bc48 to
61d5a11
Compare
There was a problem hiding this comment.
Two findings in the changed web UI. The MenuSubTrigger icon treatment added in this revision fixes the alignment of the new Browser sub-trigger, but the selector it uses also matches the trailing chevron on the two existing icon-less sub-triggers and overrides their ms-auto.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding on the new profile badge in the preview chrome row: the truncate cap doesn't actually ellipsize because Badge is a flex container. Everything else in scope (the MenuSubTrigger icon-column fix, the MenuGroup/MenuGroupLabel wrapping in PreviewMoreMenu, the profile list rows reusing SettingsRow/ITEM_ROW_INNER_CLASSNAME/DraftInput/Button/AlertDialog, and the Select for the default profile) follows the existing primitives and settings conventions.
Posted via Macroscope — UI Consistency
0009cb1 to
1adce5c
Compare
1adce5c to
36b86ed
Compare
There was a problem hiding this comment.
One convention finding: the new browserProfile module (and its test) uses the consolidated import { Schema } from "effect" form instead of the subpath namespace import used by the rest of packages/contracts. Everything else in the diff (service interfaces on BrowserSession/PreviewManager, Schema.TaggedErrorClass errors with structural attributes, make/layer shape, dependency acquisition) matches the conventions.
Posted via Macroscope — Effect Service Conventions
Profile names are user-supplied and run to 48 characters. The Browser sub-menu rendered them bare inside an unbounded popup, so a long one widened it to fit-content and wrapped; it is now capped and truncated like the other name-bearing menus. The clear actions repeated the name their own group heading already shows, which drove the popup far past its width for no added information. The heading keeps the profile and the actions keep fixed-length labels. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bare rows stack on narrow viewports with a larger gap inside a row than between rows, so the remove button read as belonging to the profile below. Each profile is now a bounded row, and the list carries the bottom spacing `SettingsRow` leaves to its children. `MenuGroupLabel` renders a block box, so `text-overflow` on an inline span inside it never applied and a long profile name pushed the popup past its width. The truncation sits on the label itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A tab created before profiles existed carries no profile of its own and runs in the built-in `default` partition — the scope the browser used before profiles. It was labelled with, and cleared against, whatever profile is configured as the default now, so on a machine with a custom default the active tab's data was left untouched while another profile's was wiped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… picker The default-profile popup rendered user-supplied names bare, so a 48-character name widened it to fit. Capped and truncated to match the tab menu's profile submenu, so the same name ellipsizes in both pickers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The earlier cap missed on two counts: `truncate` sat on an inline span inside `ItemText`'s block, where overflow rules never apply, so long names scrolled instead of ellipsizing; and `max-w-64` styled the list rather than the glass shell, which still matched the trigger width and left empty chrome beside the list when the trigger was full-width. The label is now a block, the cap applies to the shell as well, and the trigger width is not matched — the same shape as the keybinding pickers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Dropping the trigger-width match left the popup purely content-sized — about 80px under a 176px trigger with only "Default" listed. The list now fills the shell, so the shell stays at least the trigger's width (and at most 16rem), matching every other settings select. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The partition digest hashed `TextEncoder().encode(scope)`, which replaces a lone UTF-16 surrogate with U+FFFD — so two supported ids differing only by such a unit (`"p\ud800"` vs `"p�"`) landed in the same partition and shared every cookie and site storage. Lone surrogates are now escaped to `\uXXXX` before hashing, with a literal backslash doubled so the escape cannot be forged. Every well-formed scope passes through byte-for-byte unchanged, so existing partitions — and the logins in them — stay put. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4414469 to
1a5aa85
Compare
| > | ||
| {addSurfaceActions.map((action) => { | ||
| const Icon = action.icon; | ||
| // Browser collapses into one row: clicking the trigger opens |
There was a problem hiding this comment.
🟡 Medium components/RightPanelTabs.tsx:937
When the panel has no surfaces, users cannot open Incognito or a custom browser profile directly; RightPanelEmptyState only invokes onAddBrowser, which always creates the default-profile tab. The profile submenu is mounted only when props.surfaces.length > 0, so users must first create an unwanted default tab to reach it. Make the profile choices available from the empty-panel launcher as well.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/RightPanelTabs.tsx around line 937:
When the panel has no surfaces, users cannot open Incognito or a custom browser profile directly; `RightPanelEmptyState` only invokes `onAddBrowser`, which always creates the default-profile tab. The profile submenu is mounted only when `props.surfaces.length > 0`, so users must first create an unwanted default tab to reach it. Make the profile choices available from the empty-panel launcher as well.
## What's Changed * feat(desktop): browser profiles for the preview browser by @juliusmarminge in pingdotgg/t3code#7254 * refactor(shared): move the node:sqlite Effect SQL client into shared by @juliusmarminge in pingdotgg/t3code#7272 * feat(web): add opt-in panel animations by @maria-rcks in pingdotgg/t3code#8830 * feat(projects): automatically pull clean default branches by @maria-rcks in pingdotgg/t3code#9277 * fix(web): show pull request state icons in tabs by @flamboh in pingdotgg/t3code#9112 * feat(providers): add context compaction across harnesses by @maria-rcks in pingdotgg/t3code#8808 * feat(web): add proactive panels by @maria-rcks in pingdotgg/t3code#9276 * fix(web): unify control sizing across settings pages by @juliusmarminge in pingdotgg/t3code#9281 * fix(web): offer browser profiles from the empty-panel launcher by @juliusmarminge in pingdotgg/t3code#9279 * Revert "feat(providers): add context compaction across harnesses" by @maria-rcks in pingdotgg/t3code#9284 * fix(web): show scroll-to-end as soon as the last message slips under the composer by @juliusmarminge in pingdotgg/t3code#9280 * fix(cursor): honor auto and full access modes by @maria-rcks in pingdotgg/t3code#9283 * fix(desktop): detect installed Spectre libs for Windows builds by @juliusmarminge in pingdotgg/t3code#9285 * fix(pull-requests): missing features & better behaviour by @maria-rcks in pingdotgg/t3code#9188 * fix(providers): discover workspace skills everywhere by @maria-rcks in pingdotgg/t3code#9180 * fix(server): preserve automatic settlement timestamps by @eimexdev in pingdotgg/t3code#9254 * fix(opencode): show Reasoning selector for OpenCode models by @maria-rcks in pingdotgg/t3code#9287 * feat(web): preview document attachments in the file viewer by @juliusmarminge in pingdotgg/t3code#9292 * chore(ci): narrow the UI consistency check-run agent by @juliusmarminge in pingdotgg/t3code#9297 **Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1261...v0.0.39-nightly.20260903.1262 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260903.1262
Adds profiles to the desktop preview browser so tabs can keep separate cookies and site data. This is the bottom of the remaining stack; the browser-access setting is already merged.
Default preserves existing login storage. Incognito uses an in-memory partition. Other profiles use a separate partition namespace with unambiguous environment/profile scoping. A tab keeps its chosen profile for its lifetime.
Settings supports creating, renaming, choosing a default, and removing profiles. Removal waits for cookie/cache cleanup across all known environments and retains the profile if cleanup fails. Existing tabs remain open after removal and are labeled “Removed profile.” The Browser menu supports opening a chosen profile, including touch input.
Validation: focused profile, preview, settings-hydration, and partition-isolation tests; web/desktop typechecks; scoped formatting and lint. Latest CI results are shown below. Earlier desktop behavior was verified by the maintainer; this audit did not run another GUI pass.
Compatibility: Default partitions are unchanged. Data created in custom profiles by an earlier unmerged version of this stack is not migrated from the collision-prone partition mapping.
Original implementation: Claude Code. Review fixes: GPT-5.6 Sol agents, coordinated through Codex.
Note
Add browser profiles for the preview browser with per-profile partition isolation
DefaultandIncognitoprofiles, plus user-defined persistent profiles capped at a contract maximumBrowserSessionpartition derivation in BrowserSession.ts to support persistent, ephemeral, and profile-namespaced partitions, with escaping that prevents collisions from lone UTF-16 surrogates and backslash sequencesprofileIdthrough preview open, navigate, and status snapshots in Manager.ts, through webview config caching keyed by environment+profile in previewWebviewConfigState.ts, and through all desktop IPC handlers in preview.tsgetPartitionin BrowserSession.ts changes partition format for non-default profiles using a JSON tuple scope; existing sessions opened before this change remain on the legacy bare-environment persistent partition, and default-profile partitions are kept compatible with that legacy formatMacroscope summarized 1a5aa85.
Note
Medium Risk
Changes Electron partition derivation and cookie/cache clearing semantics; default profile behavior is preserved, but wrong scoping could leak or wipe the wrong profile’s storage.
Overview
Adds browser profiles so preview tabs can use separate Chromium session partitions (cookies, cache, logins). Built-in Default keeps the legacy bare-environment partition; Incognito uses a non-persistent partition; custom profiles get a namespaced partition with collision-safe scope encoding.
Contracts & server: New profile types and client settings (
browserProfiles,browserDefaultProfileId). Preview open/snapshots carry optionalprofileIdthrough navigate and status updates so a tab does not silently switch partitions.Desktop:
resolvePartitionScopemaps environment + profile to partition scope;getPreviewConfigand clear-cookie/cache IPC takeenvironmentIdand optionalprofileId, with per-profile clears loading the session first so data is actually removed after restart.Web: Settings to add/rename/remove profiles and pick a default (incognito excluded as default). New tabs and link/file opens send configured
profileId; webview config is cached per environment+profile. UI: profile submenu on “Add browser,” profile badge in preview chrome, and profile-scoped clear actions in the more menu.Note: Default partitions stay compatible; custom profiles from an earlier partition scheme are not migrated per the PR description.
Reviewed by Cursor Bugbot for commit 1a5aa85. Bugbot is set up for automated code reviews on this repo. Configure here.